
package com.heimali.happyreader.core.bean;

import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Looper;
import android.os.Message;

import com.heimali.happyreader.core.intf.IActionListener;
import com.heimali.happyreader.core.intf.IActionPublisher;
import com.heimali.happyreader.core.intf.IStoppable;
import com.heimali.happyreader.debug.Console;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;

public class ActionPublisherBase implements IActionPublisher, Callback, IStoppable {
    private HashMap<IActionListener, HashSet<Integer>> mListenerTable;
    protected final static int DELEGATE_MESSAGE = 909;
    protected Handler mUiHandler;
    private boolean mIsStopped = false;

    public ActionPublisherBase() {
        mListenerTable = new HashMap<IActionListener, HashSet<Integer>>();
        mUiHandler = new Handler(Looper.getMainLooper(), this);
    }

    @Override
    public void registerActionListener(IActionListener listener) {
        registerActionListener(listener, null);
    }

    @Override
    public synchronized void registerActionListener(IActionListener listener, int... caredActions) {
        if (mListenerTable.containsKey(listener) == true) {
            if (caredActions != null && caredActions.length != 0) {
                //appending to exiting caredActions
                final HashSet<Integer> caredActionSet = mListenerTable.get(listener);
                if (caredActionSet == null) {
                    mListenerTable.put(listener, fromArray(caredActions));
                } else {
                    for (int i : caredActions) {
                        int actionInteger = Integer.valueOf(i);
                        if (caredActionSet.contains(actionInteger) == false) {
                            caredActionSet.add(actionInteger);
                        }
                    }
                }
            }
        } else {
            if (caredActions == null || caredActions.length == 0) {
                mListenerTable.put(listener, null);
            } else {
                mListenerTable.put(listener, fromArray(caredActions));
            }
        }
    }

    @Override
    public synchronized void removeActionListener(IActionListener listener) {
        mListenerTable.remove(listener);
    }

    @Override
    public void publishAction(int actionCode, Object... args) {
        if (Looper.getMainLooper() == Looper.myLooper()) {
            publishActionInUiThread(actionCode, args);
        } else {
            mUiHandler.obtainMessage(DELEGATE_MESSAGE, actionCode, 0, args).sendToTarget();
        }
    }

    //This runs in ui thread
    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case DELEGATE_MESSAGE: {
                publishActionInUiThread(msg.arg1, (Object[]) msg.obj);
                break;
            }
        }
        return true;
    }

    private final void publishActionInUiThread(final int actionCode, final Object... args) {
        for (Entry<IActionListener, HashSet<Integer>> e : mListenerTable.entrySet()) {
            HashSet<Integer> caredActions = e.getValue();
            if (caredActions == null || caredActions.contains(Integer.valueOf(actionCode)) == true) {
                try {
                    e.getKey().actionPerformed(actionCode, args);
                } catch (Throwable ex) {
                    Console.log("error", "Error while processing action " + actionCode + ", with listener " + e.getKey().getClass().getSimpleName());
                    ex.printStackTrace();
                }
            }
        }
    }

    private final static HashSet<Integer> fromArray(int[] arrary) {
        HashSet<Integer> r = new HashSet<Integer>(arrary.length);
        for (int i : arrary) {
            r.add(i);
        }
        return r;
    }

    @Override
    public void destroyNow() {
        mListenerTable = null;
        mUiHandler = null;
    }

    @Override
    public void stopNow() {
        synchronized (this) {
            mListenerTable.clear();
        }
        mUiHandler.removeCallbacksAndMessages(null);
        mIsStopped = true;
    }

    @Override
    public boolean isStopped() {
        return mIsStopped;
    }
}
